#include "Vector2.h"

#include <Rz/Math.h>

namespace Rz { namespace Graphics { namespace Math {

Vector2::Vector2():
	X(0.0f),
	Y(0.0f)
{

}

// TODO: RZ_ASSERT
Vector2::Vector2(std::initializer_list<f32> v)
	: X(*v.begin())
	, Y(*(v.begin() + 1))
{

}

Vector2::Vector2(f32 x, f32 y):
	X(x),
	Y(y)
{

}

Vector2::Vector2(const f32* ptr):
	X(ptr[0]),
	Y(ptr[1])
{

}

Vector2::Vector2(const Vector2& vec):
	X(vec.X),
	Y(vec.Y)
{

}

void Vector2::Set(f32 x, f32 y)
{
	X = x;
	Y = y;
}

f32 Vector2::Length() const
{
	return ::Rz::Math::Sqrt(X * X + Y * Y);
}

void Vector2::Normalize()
{
	f32 len = Length();

	if (len)
	{
		len = 1.0f / len;
		X *= len;
		Y *= len;
	}
}

Vector2 Vector2::Normalized() const
{
	Vector2 result = (*this);
	result.Normalize();
	return result;
}

f32 Vector2::Dot(const Vector2& u, const Vector2& v)
{
	return u.X * v.X + u.Y * v.Y;
}

f32 Vector2::Distance(const Vector2& u, const Vector2& v)
{
	return ::Rz::Math::Sqrt((u.X - v.X) * (u.X - v.X) + (u.Y - v.Y) * (u.Y - v.Y));
}

f32 Vector2::operator[] (s32 index) const
{
	return Cell[index];
}

f32& Vector2::operator[] (s32 index)
{
	return Cell[index];
}

Vector2 Vector2::operator + (const Vector2& vec) const
{
	Vector2 result = (*this);
	result.X += vec.X;
	result.Y += vec.Y;
	return result;
}

Vector2 Vector2::operator - (const Vector2& vec) const
{
	Vector2 result = (*this);
	result.X -= vec.X;
	result.Y -= vec.Y;
	return result;
}

Vector2 Vector2::operator * (const Vector2& vec) const
{
	Vector2 result = (*this);
	result.X *= vec.X;
	result.Y *= vec.Y;
	return result;
}

Vector2 Vector2::operator / (const Vector2& vec) const
{
	Vector2 result = (*this);
	result.X /= vec.X;
	result.Y /= vec.Y;
	return result;
}

Vector2 Vector2::operator + (f32 v) const
{
	Vector2 result = (*this);
	result.X += v;
	result.Y += v;
	return result;
}

Vector2 Vector2::operator - (f32 v) const
{
	Vector2 result = (*this);
	result.X -= v;
	result.Y -= v;
	return result;
}

Vector2 Vector2::operator * (f32 v) const
{
	Vector2 result = (*this);
	result.X *= v;
	result.Y *= v;
	return result;
}

Vector2 Vector2::operator / (f32 v) const
{
	Vector2 result = (*this);
	result.X /= v;
	result.Y /= v;
	return result;
}

Vector2& Vector2::operator += (const Vector2& v)
{
	*this = *this + v;
	return *this;
}

Vector2& Vector2::operator -= (const Vector2& v)
{
	*this = *this - v;
	return *this;
}

Vector2& Vector2::operator *= (const Vector2& v)
{
	*this = *this * v;
	return *this;
}
Vector2& Vector2::operator /= (const Vector2& v)
{
	*this = *this / v;
	return *this;
}

Vector2& Vector2::operator += (f32 v)
{
	*this = *this + v;
	return *this;
}

Vector2& Vector2::operator -= (f32 v)
{
	*this = *this - v;
	return *this;
}

Vector2& Vector2::operator *= (f32 v)
{
	*this = *this * v;
	return *this;
}

Vector2& Vector2::operator /= (f32 v)
{
	*this = *this / v;
	return *this;
}

Vector2 Vector2::operator - () const
{
	Vector2 result = (*this);

	result.X = -result.X;
	result.Y = -result.Y;

	return result;
}

Vector2 operator + (f32 v, const Vector2& u)
{
	return u + v;
}

Vector2 operator - (f32 v, const Vector2& u)
{
	return u - v;
}

Vector2 operator * (f32 v, const Vector2& u)
{
	return u * v;
}

Vector2 operator / (f32 v, const Vector2& u)
{
	return u / v;
}


//////////////////////////////////////////////////////////////////////////
// IHumanReadable
String Vector2::ToString() const
{
	return String::Format("(%.3f, %.3f)", X, Y);
}

//////////////////////////////////////////////////////////////////////////
// IEquatable
RZ_INLINE bool Vector2::Equals(const Vector2& r) const
{
	return X == r.X && Y == r.Y;
}

RZ_INLINE bool Vector2::operator == (const Vector2& r) const 
{
	return  Equals(r);
}

RZ_INLINE bool Vector2::operator != (const Vector2& r) const 
{
	return !Equals(r);
}

//////////////////////////////////////////////////////////////////////////
// constants
const Vector2 Vector2::Zero    = Vector2(0.0f, 0.0f);
const Vector2 Vector2::One     = Vector2(1.0f, 1.0f);
const Vector2 Vector2::NormalX = Vector2(1.0f, 0.0f);
const Vector2 Vector2::NormalY = Vector2(0.0f, 1.0f);

} } }
